package bufferings.lab.slim3.test;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.util.concurrent.Future;
import java.util.logging.Logger;

import org.junit.Test;
import org.slim3.datastore.Datastore;
import org.slim3.tester.AppEngineTestCase;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityTranslator;
import com.google.appengine.api.datastore.Transaction;
import com.google.apphosting.api.ApiProxy;
import com.google.apphosting.api.DatastorePb;
import com.google.apphosting.api.ApiProxy.ApiConfig;
import com.google.apphosting.api.ApiProxy.ApiProxyException;
import com.google.apphosting.api.ApiProxy.Delegate;
import com.google.apphosting.api.ApiProxy.Environment;
import com.google.apphosting.api.ApiProxy.LogRecord;
import com.google.apphosting.api.DatastorePb.PutRequest;
import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;

public class Ashigeru extends AppEngineTestCase {

  private static final Logger logger =
    Logger.getLogger(Ashigeru.class.getName());

  @Test
  public void Txすり抜け疑惑_Slim3() {
    // このテストが4,5回に1回失敗する
    Entity entity = new Entity("Test", "k");
    Datastore.put(entity);

    Transaction tx = Datastore.beginTransaction();
    entity.setProperty("p", 0);
    Datastore.put(tx, entity); // これがすりぬけてる感じ
    Datastore.rollback(tx);

    assertThat(Datastore.get(entity.getKey()).getProperty("p"), is(nullValue()));
  }

  @Test
  public void Txすり抜け疑惑_LLAPI() throws Exception {
    // LLAPIでやると問題ない
    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
    Entity entity = new Entity("Test", "k");
    ds.put(entity);

    Transaction tx = ds.beginTransaction();
    entity.setProperty("p", 0);
    ds.put(tx, entity);
    tx.rollback();

    assertThat(ds.get(entity.getKey()).getProperty("p"), is(nullValue()));
  }

  @Test
  public void Txすり抜け疑惑_LLAPI_PutRequest解析用() throws Exception {
    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
    Entity entity = new Entity("Test", "k");
    ds.put(entity);

    Transaction tx = ds.beginTransaction();
    entity.setProperty("p", 0);

    // PutRequestをログ出力するDelegate
    @SuppressWarnings("unchecked")
    final Delegate<Environment> originalDelegate = ApiProxy.getDelegate();
    ApiProxy.setDelegate(new ApiProxy.Delegate<Environment>() {

      @Override
      public void log(Environment arg0, LogRecord arg1) {
        originalDelegate.log(arg0, arg1);
      }

      @Override
      public Future<byte[]> makeAsyncCall(Environment arg0, String arg1,
          String arg2, byte[] arg3, ApiConfig arg4) {
        return originalDelegate.makeAsyncCall(arg0, arg1, arg2, arg3, arg4);
      }

      @Override
      public byte[] makeSyncCall(Environment arg0, String arg1, String arg2,
          byte[] arg3) throws ApiProxyException {
        if ("datastore_v3".equals(arg1) && "Put".equals(arg2)) {
          PutRequest putRequest = new PutRequest();
          putRequest.parseFrom(arg3);
          logger.info("[putRequest]\n" + putRequest.toFlatString(true));
        }
        return originalDelegate.makeSyncCall(arg0, arg1, arg2, arg3);
      }

    });
    try{
      ds.put(tx, entity);
    }finally{
      // 戻さなくても大丈夫そうだけど一応戻しておく
      ApiProxy.setDelegate(originalDelegate);
    }
    tx.rollback();

    assertThat(ds.get(entity.getKey()).getProperty("p"), is(nullValue()));
  }

  @Test
  public void Txすり抜け疑惑_Slim3コード展開() throws Exception {
    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
    Entity entity = new Entity("Test", "k");
    ds.put(entity);

    Transaction tx = ds.beginTransaction();
    entity.setProperty("p", 0);

    // DatastoreUtil.put start
    EntityProto entityProto = EntityTranslator.convertToPb(entity);
    PutRequest putRequest = new PutRequest();
    long handle = Long.valueOf(tx.getId());
    logger.info("tx.getId() = 0x"
      + Long.toHexString(handle)
      + "("
      + handle
      + ")");
    if (handle >= 0) {
      DatastorePb.Transaction tx2 = new DatastorePb.Transaction();
      tx2.setHandle(handle);
      tx2.setApp(ApiProxy.getCurrentEnvironment().getAppId());
      putRequest.setTransaction(tx2);
    }
    putRequest.addEntity(entityProto);
    logger.info("[putRequest(after addEntity)]\n"
      + putRequest.toFlatString(true));

    byte[] requestBuf = putRequest.toByteArray();
    ApiProxy.makeSyncCall("datastore_v3", "Put", requestBuf);
    // DatastoreUtil.put end

    tx.rollback();
    assertThat(ds.get(entity.getKey()).getProperty("p"), is(nullValue()));
  }
}
